package com.sudo.service.admin.rpc.provider;

import com.sudo.common.component.SpringContextUtil;
import com.sudo.common.utils.HttpServletUtil;
import com.sudo.common.utils.ThreadLocalUtil;
import com.sudo.rpc.api.common.RpcConstant;
import com.sudo.rpc.api.model.CallTask;
import com.sudo.rpc.api.model.RegistryServer;
import com.sudo.rpc.api.service.RmiSetServerService;
import com.sudo.rpc.api.utils.RpcRes;
import com.sudo.service.admin.runnable.NextJobUtil;
import com.sudo.service.admin.service.LogAndEmailService;
import com.sudo.service.admin.service.SysServerNodeService;
import com.sudo.service.admin.service.SysServerNodeTaskService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.RemoteServer;
import java.rmi.server.ServerNotActiveException;
import java.rmi.server.UnicastRemoteObject;
import java.util.Map;
import java.util.Objects;

@Slf4j
@Service
public class RmiRegistryServiceImpl extends UnicastRemoteObject implements RmiSetServerService {

    @Resource
    private transient SysServerNodeService sysServerNodeService;

    @Resource
    private transient SysServerNodeTaskService sysServerNodeTaskService;

    @Resource
    private transient LogAndEmailService logAndEmailService;

    public RmiRegistryServiceImpl() throws RemoteException {
        super();
    }

    // 执行器启动时调用，向管理端注册rmi地址
    @Override
    public void pushServer(RegistryServer registryServer) throws RemoteException {
        try {
            String clientHost = RemoteServer.getClientHost();
            String server = clientHost + ":" + registryServer.getPort();
            log.info("一个服务来注册：{}",server);

            sysServerNodeService.deleteAndSaveServerNode(server);
            sysServerNodeTaskService.deleteAndSaveServerNodeTask(server, registryServer);
        } catch (ServerNotActiveException e) {
            log.error("服务注册异常：",e);
        }
    }

    @Override
    public Object pullServer(String ip, Integer port) throws RemoteException {
        try {
            Registry remoteRegistry = LocateRegistry.getRegistry(ip, port);
            RmiSetServerService rmiSetServerService = (RmiSetServerService) remoteRegistry.lookup(RpcConstant.registryCenter);
            return RpcRes.ok(rmiSetServerService.pullServer(ip, port));
        } catch (NotBoundException e) {
            log.error("服务未绑定异常：",e);
            return RpcRes.errorMsg("服务未绑定异常：" + e.getCause());
        }
    }

    @Override
    public Object callServer(CallTask callTask) throws RemoteException {
        log.info("rpc任务开始执行，服务：{}:{}，模块：{}，方法：{}，参数：{}，回调：{}",callTask.getIp(),callTask.getPort(),callTask.getBeanName(),callTask.getMethodName(),callTask.getMethodParams(),callTask.getCallbackFun());
        long startTime = System.currentTimeMillis();
        Map<String, Object> tlMap = ThreadLocalUtil.getTlMap();
        ThreadLocalUtil.removeTlMap();
        int currentJobId = Integer.parseInt(tlMap.get("tl_jobId").toString());
        try {
            Registry remoteRegistry = LocateRegistry.getRegistry(callTask.getIp(), callTask.getPort());
            RmiSetServerService rmiSetServerService = (RmiSetServerService) remoteRegistry.lookup(RpcConstant.registryCenter);
            Object o = rmiSetServerService.callServer(callTask);

            // rpc任务调用后的 日志，邮件处理
            RpcRes rpcRes = (RpcRes) o;
            // 有回调
            if (rpcRes.getStatus().compareTo(RpcConstant.callCode) == 0) {
                if (StringUtils.isNotBlank(callTask.getCallbackFun())) {
                    String[] beanMethod = callTask.getCallbackFun().split("/");

                    try {
                        Object targetBean = SpringContextUtil.getBean(beanMethod[0]);
                        // 找到带有三个参数的回调方法Integer jobId，CallTask callTask，RpcRes rpcRes
                        Method method = targetBean.getClass().getDeclaredMethod(beanMethod[1], Integer.class, CallTask.class, RpcRes.class);
                        method.invoke(targetBean, currentJobId, callTask, rpcRes);
                        rpcRes.setStatus(RpcConstant.successCode);
                        rpcRes.setMsg("回调成功");
                    } catch (NoSuchMethodException e) {
                        rpcRes.setStatus(RpcConstant.exceptionCode);
                        rpcRes.setMsg("回调失败，找不到回调方法：" + beanMethod[1]);
                        String exception = ExceptionUtils.getStackTrace(e);
                        rpcRes.setData(exception);
                        log.error("回调失败，找不到回调方法：{}", beanMethod[1], e);
                    } catch (InvocationTargetException e) {
                        rpcRes.setStatus(RpcConstant.exceptionCode);
                        rpcRes.setMsg("回调失败，调用方法" + beanMethod[1] + "异常：" + e.getCause());
                        String exception = ExceptionUtils.getStackTrace(e);
                        rpcRes.setData(exception);
                        log.error("回调失败，调用方法异常", e);
                    } catch (IllegalAccessException e) {
                        rpcRes.setStatus(RpcConstant.exceptionCode);
                        rpcRes.setMsg("回调失败，非法访问异常");
                        String exception = ExceptionUtils.getStackTrace(e);
                        rpcRes.setData(exception);
                        log.error("回调失败，非法访问异常", e);
                    } catch (NoSuchBeanDefinitionException e) {
                        rpcRes.setStatus(RpcConstant.exceptionCode);
                        rpcRes.setMsg("回调失败，找不到bean异常");
                        String exception = ExceptionUtils.getStackTrace(e);
                        rpcRes.setData(exception);
                        log.error("回调失败，找不到bean异常", e);
                    }
                } else {
                    rpcRes.setStatus(RpcConstant.exceptionCode);
                    rpcRes.setMsg("调用成功，但缺少回调方法");
                    log.error("Rpc任务【{}】,{}-{}-{}-{}缺少回调方法.", tlMap.get("tl_jobId").toString(), callTask.getIp(), callTask.getPort(), callTask.getBeanName(), callTask.getMethodName());
                }
            }
            logAndEmailService.asyncRpcMethodJobLogAndEmail(HttpServletUtil.getResponse(), tlMap, rpcRes);
            // 调用子任务
            NextJobUtil.runNextRpcJobs(currentJobId, rpcRes.getStatus());
            return rpcRes;
        } catch (NotBoundException e) {
            log.error("服务未绑定异常：", e);
            // 调用子任务
            NextJobUtil.runNextRpcJobs(currentJobId, RpcConstant.exceptionCode);
            return RpcRes.errorMsg("服务未绑定异常：" + e.getCause());
        } catch (RemoteException e) {
            // 调用子任务
            NextJobUtil.runNextRpcJobs(currentJobId, RpcConstant.exceptionCode);
            return RpcRes.errorMsg("服务不可用异常：" + e.getCause());
        }finally {
            long endTime = System.currentTimeMillis();
            log.info("rpc任务执行完成，服务：{}:{}，模块：{}，方法：{}，参数：{}，回调：{}，耗时：{}毫秒",callTask.getIp(),callTask.getPort(),callTask.getBeanName(),callTask.getMethodName(),callTask.getMethodParams(),callTask.getCallbackFun(),endTime-startTime);
        }
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof RmiRegistryServiceImpl)) return false;
        if (!super.equals(o)) return false;
        RmiRegistryServiceImpl that = (RmiRegistryServiceImpl) o;
        return Objects.equals(sysServerNodeService, that.sysServerNodeService) && Objects.equals(sysServerNodeTaskService, that.sysServerNodeTaskService) && Objects.equals(logAndEmailService, that.logAndEmailService);
    }

    @Override
    public int hashCode() {
        return Objects.hash(super.hashCode(), sysServerNodeService, sysServerNodeTaskService, logAndEmailService);
    }
}
